#include "VertexProcessor.h"

namespace MASGK
{

	CVertexProcessor::CVertexProcessor()
	{
		m_Obj2World = float4x4();
		m_World2View = float4x4();
		m_View2Proj = float4x4();
		m_Eye = float3();

		SetIdentity(m_Obj2World);
		SetIdentity(m_World2View);
		SetIdentity(m_View2Proj);

		// Testing pixel light
		m_PointLight = CPointLight
							(
                                float3(sin(0.0f), 0.0f, 1.0f),	// position
								float3(1.0f, 0.0f, 0.0f),		// color
								2.0f,							// intensity
								Attenuation(0.0f, 0.0f, 1.0f),	// constant, linear, exponent
								4.0f							// range
							);
	}

	void CVertexProcessor::SetPerspective(float _fov, float _aspect, float _near, float _far)
	{
		_fov *= PI / 360.0f;
		float f = cos(_fov) / sin(_fov);
		m_View2Proj[0] = float4(f / _aspect, 0, 0, 0);
		m_View2Proj[1] = float4(0, f, 0, 0);
		m_View2Proj[2] = float4(0, 0, (_far + _near) / (_near - _far), -1.0f);
		m_View2Proj[3] = float4(0, 0, (2.0f * _far * _near) / (_near - _far), 0);
	}

	void CVertexProcessor::SetLookAt(float3 _eye, float3 _center, float3 _up)
	{
		m_Eye = _eye;
		float3 f = _center - _eye;
		f.normalizeMe();
		_up.normalizeMe();
		float3 s = cross(f, _up);
		float3 u = cross(s, f);
		m_World2View[0] = float4(s.x, u.x, -f.x, 0);
		m_World2View[1] = float4(s.y, u.y, -f.y, 0);
		m_World2View[2] = float4(s.z, u.z, -f.z, 0);
		m_World2View[3] = float4(-_eye.x, -_eye.y, -_eye.z, 1);
	}

	void CVertexProcessor::SetIdentity(float4x4 &_m)
	{
		_m[0] = float4(1.0f, 0, 0, 0);
		_m[1] = float4(0, 1.0f, 0, 0);
		_m[2] = float4(0, 0, 1.0f, 0);
		_m[3] = float4(0, 0, 0, 1.0f);
	}

	void CVertexProcessor::MultByTranslation(float3 _v)
	{
		float4x4 m
			(
				float4(1.0f, 0, 0, 0),
				float4(0, 1.0f, 0, 0),
				float4(0, 0, 1.0f, 0),
				float4(_v.x, _v.y, _v.z, 1.0f)
			);

		m_Obj2World = m * m_Obj2World;
	}

	void CVertexProcessor::MultByScale(float3 _v)
	{
		float4x4 m
			(
				float4(_v.x, 0, 0, 0),
				float4(0, _v.y, 0, 0),
				float4(0, 0, _v.z, 0),
				float4(0, 0, 0, 1.0f)
			);

		m_Obj2World = m * m_Obj2World;
	}

	void CVertexProcessor::MultByRotation(float _alpha, float3 _v)
	{
		float s = sin(_alpha * PI / 180.0f);
		float c = cos(_alpha * PI / 180.0f);
		_v.normalizeMe();
		
		float4x4 m
			(
				float4(c + (_v.x * _v.x) * (1.0f - c), _v.y * _v.x * (1.0f - c) + _v.z * s, _v.z * _v.x * (1.0f - c) - _v.y * s, 0),
				float4(_v.x * _v.y * (1.0f - c) - _v.z * s, c + (_v.y * _v.y) * (1.0f - c), _v.z * _v.y * (1.0f - c) + _v.x * s, 0),
				float4(_v.x * _v.z * (1.0f - c) + _v.y * s, _v.y * _v.z * (1.0f - c) - _v.x * s, c + (_v.z * _v.z) * (1.0f - c), 0),
				float4(0, 0, 0, 1.0f)
			);

		m_Obj2World = m * m_Obj2World;
	}

	float3 CVertexProcessor::Tr(float3 _vertex)
	{
		float4 vertex = float4(_vertex.x, _vertex.y, _vertex.z, 1.0f);
		//float4 retV = mul(vertex, m_ModelViewProj);
		float4 retV = m_ModelViewProj * vertex;

		// http://en.wikipedia.org/wiki/3D_projection
		// Or, in matrix form using homogeneous coordinates, the system
		float4x4 hom = float4x4
				(
					float4(1.0f, 0, 0, 0),
					float4(0, 1.0f, 0, 0),
					float4(-(m_Eye.x / m_Eye.z), -(m_Eye.y / m_Eye.z), 1.0f, 1.0f / m_Eye.z),
					float4(0, 0, 0, 0)
				);
		float4 d = retV;	//	camera position = (0, 0, 0)
		float4 f = hom * float4(d.x, d.y, d.z, 1.0f);

		//return float3(retV.x, retV.y, retV.z);
		return float3(f.x / f.w, f.y / f.w, retV.z);
	}

	void CVertexProcessor::CreateView()
	{
		SetIdentity(m_ModelViewProj);
		m_ModelViewProj = m_View2Proj * m_World2View * m_Obj2World;
	}

	float3 CVertexProcessor::CountNormal(float3& _A, float3& _B, float3& _C)
	{
		return cross(_B - _A, _C - _A);
	}

	void CVertexProcessor::ResetModelMatrix()
	{
		SetIdentity(m_Obj2World);
	}
}
